package sm;

import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;
import com.sun.spot.util.Queue;
import sm.policy.FreqAdaptPolicy;

//import com.sun.spot.sensorboard.EDemoBoard;
//import com.sun.spot.sensorboard.peripheral.ITemperatureInput;
//import java.io.IOException;



public class TempService extends SensingService implements Runnable{
	 private static TempService ref;
     private static final int eventId = 2;
     private static long sampleFreq = 55;
     private Hashtable requestedDataHash = new Hashtable();
 	 private static byte energyCategory = 1;
 	 //concurrency
 	 private TimedQueue queue = new TimedQueue();
     
     private TempService(){
    	 ServiceRegistry.getInstance().registerService("2", eventId);
     }	
     public static synchronized TempService getInstance(){
         if (ref == null)
            ref = new TempService();
          return ref;
    }
     @Override
     public synchronized int  getEventId(){
    	return eventId;
     }
 //    public double getTemperature() throws IOException{
 //     System.out.println("temp C " + tempSensor.getCelsius());
 //     return tempSensor.getCelsius();
 //    }
    
     @Override
     public synchronized long getSampleFreq(){
    	 return sampleFreq;
     }
     @Override    
     public synchronized double getValue(){
  	   return 335.123;
     }
     @Override
     public synchronized Hashtable getValue(long requestId){
  	   Long requestIdKey = new Long(requestId);
  	   Hashtable hashResult = (Hashtable)(requestedDataHash.get(requestIdKey));
	   if (hashResult == null){
		   Hashtable hashForNull = new Hashtable();
		  return hashForNull;
	   } else 
		   return hashResult;
     }     
     @Override
     public void processRequest(long requestId, long sampleFreq1, long duration){
  	   Long requestIdKey2 = new Long(requestId);
  	   long currentTime = System.currentTimeMillis();
  	   long endTime = currentTime + duration;
  	   Hashtable localDataHash = new Hashtable();
  	   
  	   System.out.println(System.currentTimeMillis() + " processRequest() " + requestIdKey2.toString());
  	   this.adapt();
  	   if ( isValidRequest(requestId, sampleFreq1, duration)){	
  		   System.out.println(System.currentTimeMillis() + " processRequest() en IF " + requestIdKey2.toString());
  			   for (long time = 0 ; time < endTime; time = System.currentTimeMillis()  ){
  				   Date date = new Date();
  				   Long dateKey = date.getTime();
  				   localDataHash.put(dateKey, new Double(this.getValue()));
  				   if (requestedDataHash.contains(requestIdKey2)){
  					   requestedDataHash.remove(requestIdKey2);
  					   requestedDataHash.put(requestIdKey2, localDataHash);
  				   }else {
  					   requestedDataHash.put(requestIdKey2, localDataHash);
  				   }
  				   try {
  							Thread.sleep(sampleFreq1);
  						} catch (InterruptedException e) {
  							// TODO Auto-generated catch block
  							e.printStackTrace();
  						}
  			   }
  	   } else {
  		   System.out.println("sampling frequency not supported or repeated requestId");
  	   }
     }     
     private void adapt(){
  	   this.adaptSampleFreq();
     }
     @Override
     protected synchronized void adaptSampleFreq(){
  	   FreqAdaptPolicy policy = new FreqAdaptPolicy();
  	   sampleFreq = policy.checkFreq(energyCategory); 
     }
     @Override
     public synchronized boolean isValidRequest(long requestId, long sampleFreq1, long duration){
  	   if ((!isValidFreq(sampleFreq1)) || (requestedDataHash.containsKey(requestId))){
  			return false;
  	   }
  	   return true;
     }
     @Override
     public synchronized boolean isValidFreq(long sampleFreq1){
  	   return (sampleFreq1 >= sampleFreq);
     }
     @Override
	 public synchronized Hashtable getAllData(){
	   return requestedDataHash;
	 }
     @Override
	 public void run(){
    	 RequestObject requestObject1 = (RequestObject)queue.get();    
    	 System.out.println(System.currentTimeMillis() + " temp service in new thread " + requestObject1.getRequestId());
    	 this.processRequest(requestObject1.getRequestId(), requestObject1.getSampleFreq(), requestObject1.getDuration());
    	 System.out.println(System.currentTimeMillis() + " finished request for temp " + requestObject1.getRequestId());
    	 Thread.yield();
	   }
	   @Override
	   public synchronized void processRequestThread(long requestId, long sampleFreq1, long duration){
		long sleep2 = 5;
		Long requestIdLong = new Long(requestId);		
		RequestObject requestObject =  new RequestObject();
		requestObject.setRequestId(requestId);
		requestObject.setSampleFreq(sampleFreq1);
		requestObject.setDuration(duration);
		
		queue.put(requestObject);
		System.out.println(System.currentTimeMillis() + " queue size after put " + 
				requestIdLong.toString()+ "  " + queue.size());
		
		Runnable runnable = TempService.getInstance();
		Thread thread = new Thread(runnable, requestIdLong.toString());
		System.out.println(System.currentTimeMillis() + " starting thread " + thread.getName());
		thread.start();	   
	   }
    public  class RequestObject{
    	long requestIdObject;
    	long sampleFreqObject;
    	long durationObject;
    	
    	public RequestObject(){    		
    	}
    	public long getRequestId(){
    		return this.requestIdObject;
    	}
    	public void setRequestId(long requestId){
    		this.requestIdObject = requestId;
    	}
    	public long getSampleFreq(){
    		return this.sampleFreqObject;
    	}
    	public void setSampleFreq(long sampleFreq){
    		this.sampleFreqObject = sampleFreq;
    	}
    	public long getDuration(){
    		return this.durationObject;
    	}
    	public void setDuration(long duration){
    		this.durationObject = duration;
    	}
    }
    

    public class TimedQueue extends Queue{
    	private Vector v = new Vector();
    	private boolean stopped = false;
    	private long waiting = 100;
    	private long timed = System.currentTimeMillis();
    	
    	/**
    	 * Answer whether the receiver is empty.
    	 * 
    	 * @return - whether the receiver is empty.
    	 */
    	public boolean isEmpty() {
    		synchronized (v) {
    			return v.isEmpty();
    		}		
    	}
    	
    	/**
    	 * Answer the number of elements in the receiver.
    	 * 
    	 * @return -- the number of elements in the receiver.
    	 */
    	public int size() {
    		synchronized (v) {
    			return v.size();
    		}		
    	}
    	
    	/**
    	 * Add an element to the receiver.
    	 *  
    	 * @param o -- the Object to add
    	 */
    	public void put(Object o) {
    		synchronized (v) {
    			v.addElement(o);
    			v.notify();
    		}
    	}

    	/**
    	 * Answer an element from the receiver. If the receiver is empty, block
    	 * until either an element becomes available, or for timeout milliseconds,
    	 * or the queue is stopped. In the event of timeout, return null.
    	 * Note that if a waiting thread is interrupted it will return (probably null).
    	 * 
    	 * @param timeout -- number of milliseconds to wait
    	 * @return -- either the first element from the receiver or null after a timeout or if queue is stopped.
    	 */
    	public Object get(long timeout) {
    		synchronized (v) {
    			if (timeout > 0 && v.isEmpty() && !stopped) {
    				try {
    					v.wait(timeout);
    				} catch (InterruptedException e) {
    					// it's important to catch this exception because we don't notifyAll in
    					// put(), so only one thread is unblocked and the thread must consume the
    					// element even if it has been interrupted
    				}
    			}
    			return pop();
    		}		
    	}

    	/**
    	 * Answer an element from the receiver. If the receiver is empty, block
    	 * (possibly forever) until an element becomes available.
    	 * Note that if a waiting thread is interrupted it will return (probably null).
    	 * 
    	 * @return -- the first element from the receiver, or null if queue is stopped
    	 */
    	public synchronized Object get() {
    		synchronized (v) {
    			while (v.isEmpty() && !stopped) {
    				try {
    					v.wait();
    				} catch (InterruptedException e) {
    					// it's important to catch this exception because we don't notifyAll in
    					// put(), so only one thread is unblocked and the thread must consume the
    					// element even if it has been interrupted
    					break;
    				}
    			}
    			while (System.currentTimeMillis() < timed){
    				
    			}    			
				//System.out.println(System.currentTimeMillis()+ " in get after waiting");
				timed = System.currentTimeMillis() + waiting;
    			return pop(); 
    		}		
    	}/* for the timed queue to work i had to avoid calling any other methods on the queue
    	after the get. I tried a queue.size() and it did not execute it until the next request
    	got a return. so a synch queue does only 1 operation at a time on any method.
    	
    	/**
    	 * Release all waiters
    	 */
    	public void stop() {
    		synchronized (v) {
    			stopped = true;
    			v.notifyAll();
    		}		
    	}
    	
    	/**
    	 * Drop the contents of the queue.
    	 */
    	public void empty() {
    		synchronized (v) {
    			v.removeAllElements();
    		}		
    	}

    	private Object pop() {
    		Object result = null;
    		if (!v.isEmpty()) {
    			result = v.firstElement();
    			v.removeElementAt(0);
    		}
    		return result;
    	}
    }
    /*   class Task extends TimerTask {
    public void run() {
		Runnable runnable = TempService.getInstance();
		Thread thread = new Thread(runnable);
		System.out.println(System.currentTimeMillis() + " starting thread after timer " + thread.getName());
		thread.start();
        System.out.println(System.currentTimeMillis() + " end of TimerTask run() ");
    }
}
*/
    
}
